MIT6.s081实验记录(一):gdb调试qemu方法 & lab1

您所在的位置:网站首页 qemu gdb教程 MIT6.s081实验记录(一):gdb调试qemu方法 & lab1

MIT6.s081实验记录(一):gdb调试qemu方法 & lab1

2024-07-17 14:16| 来源: 网络整理| 查看: 265

MIT6.s081实验记录(一):gdb调试qemu方法 & lab1 准备工作 xv6调试

在xv6文件夹下make qemu-gdb启动qemu上的gdbserver。

xv6的ISA是riscv,所以我们需要使用riscv的调试器riscv64-unknown-elf-gdb来调试xv6。

使用tmux分屏,在另一窗口中,在相同的xv6文件夹下riscv64-unknown-elf-gdb 再加上要调试的文件名(比如kernel/kernel或者user/_sleep),也可以进入gdb后再输入file user/_[execname]加载可执行文件。

可以看到riscv的gdb跑起来了,输入:

(gdb) target remote localhost:26000

与qemu中的gdbserver建立连接,不然会提醒你the program is not being run.

现在可以看到以下输出

Remote debugging using localhost:26000 0x0000000000001000 in ?? ()

每次都要输入target remote localhost:26000很麻烦,根据提示

image-20210914144855599

我们在用户目录下的.gdbinit文件中添加以下内容即可:

add-auto-load-safe-path /home/bolee/s081/xv6-labs-2020/.gdbinit

通过b [linenum]设置断点。gdb在user程序中打断点会出现Cannot access memory at address错误,需要自己在.gdbinit.tmpl-riscv里加一行set riscv use-compressed-breakpoints yes。

设置了断点后再输入c继续,此时gdb窗口会卡住,等待qemu中的相关程序执行

image-20210914143134537

在make qemu-gdb的终端中执行该可执行文件,此时才可以在gdb窗口中开始debug

image-20210914143301478 编译和测试

在编写好程序之后,需要在makefile中把我们写好的sleep.c加进去:

UPROGS=\ $U/_cat\ $U/_echo\ $U/_forktest\ ........ $U/_kalloctest\ $U/_bcachetest\ $U/_alloctest\ $U/_bigfile\ $U/_sleep\

make qemu就会将我们加入的程序编译,sleep.c就会被编译成可执行文件_sleep,并保存在xv6的文件系统中,然后就可以在xv6 shell中运行我们写好的程序。

我们可以在user目录下看到我们编译生成的可执行文件,

image-20210914120101664

这些可执行文件的格式为riscv,无法在本地x86的机器上执行,只能在qemu中模拟的riscv环境上运行

image-20210914120422533

运行make grade可以测试我们的程序是否正确。make grade会运行所有的程序,如果只是想针对某一个assignment运行测试,运行

$ ./grade-lab-util sleep

或者

$ make GRADEFLAGS=sleep grade

以上会针对sleep程序进行测试。

sleep

Implement the UNIX program sleep for xv6; your sleep should pause for a user-specified number of ticks. A tick is a notion of time defined by the xv6 kernel, namely the time between two interrupts from the timer chip.

在kernel/sysproc.c 中查看System系统调用的实现,在user/user.h中查看可从用户程序调用的sleep的 C 定义,在user/usys.s中查看从用户代码跳转到内核以进行sleep的汇编代码。

可以使用atoi将传入的字符串转为integer,该函数返回转换后的长整数,如果没有执行有效的转换,则返回零。

#include "../kernel/types.h" #include "user.h" int main(int argc, char *argv[]) { if(argc != 2){ // 向标准错误中输出错误信息 fprintf(2,"you must input sleep time"); exit(1); } // atoi将字符串转为int,再进行系统调用sleep sleep(atoi(argv[1])); exit(0); } pingpong

编写一个程序,使用 UNIX 系统调用在两个进程之间通过一对管道“ping-pong”一个字节,每个管道一个。 父进程应该向子进程发送一个字节; 子进程应该打印“: received ping”,其中 是它的进程 ID,将管道上的字节写入父进程,然后退出; 父进程应该从子进程那里读取字节,打印“: received pong”,然后退出。我们的解决方案应该在文件 user/pingpong.c 中。

#include "../kernel/types.h" #include "user.h" #define READ_END 0 #define WRITE_END 1 int main(int argc,char *argv[]) { int p_child[2]; int p_parent[2]; char buf[1]; pipe(p_child); pipe(p_parent); // 子进程 if(fork() == 0){ close(p_child[READ_END]); close(p_parent[WRITE_END]); read(p_parent[READ_END],buf,1); printf("%d: received ping\n", getpid()); write(p_child[WRITE_END]," ",1); close(p_child[WRITE_END]); close(p_parent[READ_END]); exit(0); }else{ // 关闭父进程接收管道的写入端描述符 close(p_child[WRITE_END]); // 关闭父进程输出管道的接收端描述符 close(p_parent[READ_END]); write(p_parent[WRITE_END]," ",1); read(p_child[READ_END],buf,1); printf("%d: received pong\n", getpid()); close(p_child[READ_END]); close(p_parent[WRITE_END]); exit(0); } } primes

使用管道编写并发版本的素数筛

Your goal is to use pipe and fork to set up the pipeline. The first process feeds the numbers 2 through 35 into the pipeline. For each prime number, you will arrange to create one process that reads from its left neighbor over a pipe and writes to its right neighbor over another pipe.

image-20210118093050894 #include "../kernel/types.h" #include "user.h" #define MAX_NUM 35 #define READ_END 0 #define WRITE_END 1 void func(int); int main() { int p[2]; pipe(p); if(fork() == 0){ close(p[WRITE_END]); func(p[READ_END]); }else{ // READ_END被传递给下一个进程, // 在本进程中不再使用,需要关闭 close(p[READ_END]); for(int i=2;i


【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3